home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 6 / develop 6 code / TCP / NewsWatcher / NewsWatcher 2.0d15 source / source / key.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-09  |  12.5 KB  |  506 lines  |  [TEXT/KAHL]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     key.c
  4.  
  5.     This module handles key down events.
  6.     
  7.     Portions copyright © 1990, Apple Computer.
  8.     Portions copyright © 1993, Northwestern University.
  9.  
  10. ----------------------------------------------------------------------------*/
  11.  
  12. #include <packages.h>
  13. #include <ctype.h>
  14. #include <string.h>
  15. #include <stdio.h>
  16.  
  17. #include "glob.h"
  18. #include "close.h"
  19. #include "cmd.h"
  20. #include "collapse.h"
  21. #include "key.h"
  22. #include "mark.h"
  23. #include "next.h"
  24. #include "scroll.h"
  25. #include "util.h"
  26. #include "wind.h"
  27. #include "subject.h"
  28. #include "article.h"
  29. #include "dlgutil.h"
  30.  
  31.  
  32. /*    CompareGroups is used by HandleListKey below to compare a typed keyboard
  33.     navigation string to a group name. Periods in the keyboard navigation string
  34.     match everything up to and including the next period in the group name. Thus,
  35.     for example, "c.s.m.a" matches "comp.sys.mac.announce".
  36. */
  37.  
  38. static short CompareGroups (char *a, char *b)
  39. {
  40.     char aup, bup;
  41.  
  42.     while (*a != 0) {
  43.         if (*a == '.') {
  44.             while (*b != 0 && *b != '.') b++;
  45.             if (*b == 0) {
  46.                 return 1;
  47.             } else {
  48.                 b++;
  49.             }
  50.         } else {
  51.             aup = toupper(*a);
  52.             bup = toupper(*b);
  53.             if (aup < bup) return -1;
  54.             if (aup > bup) return 1;
  55.             b++;
  56.         }
  57.         a++;
  58.     }
  59.     return 0;
  60. }
  61.  
  62.  
  63.  
  64. /*    DoListArrow handles the up and down arrow keys in list windows. */
  65.  
  66. static void DoListArrow (WindowPtr wind, Boolean up)
  67. {
  68.     TWindow **info;
  69.     ListHandle theList;
  70.     Cell tmpCell;
  71.  
  72.     info = (TWindow**)GetWRefCon(wind);
  73.     theList = (**info).theList;
  74.     SetPt(&tmpCell,0,0);
  75.     while (LGetSelect(true,&tmpCell,theList)) {
  76.         LSetSelect(false,tmpCell,theList);
  77.         tmpCell.v++;
  78.     }
  79.     if (up) {
  80.         tmpCell.v -= 2;
  81.         if (tmpCell.v < 0) tmpCell.v = 0;
  82.     } else { /* down */
  83.         if (tmpCell.v >= (**theList).dataBounds.bottom) tmpCell.v--;
  84.     }
  85.     LSetSelect(true,tmpCell,theList);
  86.     LAutoScroll(theList);
  87. }        
  88.  
  89.  
  90. /*    OpenSelectedCells opens all selected items in a list. The items are opened
  91.     in reverse order, so that the window for the earliest one appears on top.
  92. */
  93.  
  94. void OpenSelectedCells (WindowPtr wind)
  95. {
  96.     Cell theCell;
  97.     TWindow **theInfo;
  98.     ListHandle theList;
  99.     Boolean error=false;
  100.     EWindowKind kind;
  101.     short *p,*pBegin,result,numSelected=0,numOpened=0;
  102.     CStr255 msg;
  103.     
  104.     theInfo = (TWindow**) GetWRefCon(wind);
  105.     theList = (**theInfo).theList;
  106.     kind = (**theInfo).kind;
  107.     
  108.     SetPt(&theCell,0,(**theList).dataBounds.bottom-1);
  109.     while (theCell.v >= 0) {
  110.         pBegin = (**theList).cellArray;
  111.         p = pBegin + theCell.v;
  112.         while (*p >= 0 && p >= pBegin) p--;
  113.         theCell.v = p - pBegin;
  114.         if (p >= pBegin) {
  115.             switch (kind) {
  116.                 case kFullGroup:
  117.                 case kNewGroup:
  118.                 case kUserGroup:
  119.                     result = OpenGroupCell(wind, theCell);
  120.                     break;
  121.                 case kSubject:
  122.                     result = OpenSubjectCell(wind, theCell, 1);
  123.                     break;
  124.             }
  125.             numSelected++;
  126.             if (result == 2) return;
  127.             if (result == 0) numOpened++;
  128.         }
  129.         theCell.v--;
  130.     }
  131.     if (numOpened < numSelected) {
  132.         if (kind == kSubject) {
  133.             if (numSelected == 1) {
  134.                 strcpy(msg,"The selected article could not be opened");
  135.                 strcat(msg,", because it no longer exists on the news server.");
  136.             } else {
  137.                 if (numOpened == 0) {
  138.                     strcpy(msg,"None of the selected articles could be opened");
  139.                 } else {
  140.                     strcpy(msg,"Some of the selected articles could not be opened");
  141.                 }
  142.                 strcat(msg,", because they no longer exist on the news server.");
  143.             }
  144.         } else {
  145.             if (numSelected == 1) {
  146.                 strcpy(msg,"The selected group was not opened");
  147.                 if (kind == kUserGroup) {
  148.                     strcat(msg,", because it contains no unread articles.");
  149.                 } else {
  150.                     strcat(msg,", because it contains no articles.");
  151.                 }
  152.             } else {
  153.                 if (numOpened == 0) {
  154.                     strcpy(msg,"None of the selected groups were opened");
  155.                     if (kind == kUserGroup) {
  156.                         strcat(msg,", because they contain no unread articles.");
  157.                     } else {
  158.                         strcat(msg,", because they contain no articles.");
  159.                     }
  160.                 } else {
  161.                     *msg = 0;
  162.                 }
  163.             }
  164.         }
  165.         if (*msg != 0) ErrorMessage(msg);
  166.     }
  167. }
  168.  
  169.  
  170. /*    HandleListKey handles keydown events in list manager windows.
  171. */
  172.  
  173. static void HandleListKey (WindowPtr wind, char theChar, short modifiers)
  174. {
  175.     ListHandle theList;
  176.     static long lastDown = 0;
  177.     static short numChars = 0;
  178.     static CStr255 searchStr;
  179.     Cell theCell,tmpCell;
  180.     TWindow **info;
  181.     Boolean isSorted;
  182.     short cellData,cellDataLen;
  183.     TGroup **groupArray;
  184.     char *theString;
  185.     short low,high,compare;
  186.     Handle strings;
  187.     EWindowKind kind;
  188.     
  189.     info = (TWindow**)GetWRefCon(wind);
  190.     kind = (**info).kind;
  191.     theList = (**info).theList;
  192.  
  193.     switch (theChar) {
  194.         case returnKey:
  195.         case enterKey:
  196.             OpenSelectedCells(wind);
  197.             return;
  198.         case upArrow:
  199.             DoListArrow(wind, true);
  200.             break;
  201.         case downArrow:
  202.             DoListArrow(wind, false);
  203.             break;
  204.         case rightArrow:
  205.         case leftArrow:
  206.             if (kind == kSubject && (modifiers & cmdKey))
  207.                 ExpandCollapseKey(wind, theChar);
  208.             break;
  209.         default:
  210.             if (kind == kSubject) return;
  211.             if (modifiers & cmdKey) return;
  212.             SetPt(&theCell,0,0);
  213.             if ((TickCount()-lastDown) > (4*GetDblTime()))
  214.                 numChars = 0;
  215.             lastDown = TickCount();
  216.             searchStr[numChars++] = theChar;
  217.             searchStr[numChars] = 0;
  218.             isSorted = (kind == kFullGroup) || (kind == kNewGroup);
  219.             strings = (**info).strings;
  220.             HLock(strings);
  221.             if (isSorted) {
  222.                 groupArray = (**info).groupArray;
  223.                 low = 0;
  224.                 high = (**theList).dataBounds.bottom - 1;
  225.                 while (low < high) {
  226.                     theCell.v = (low + high) >> 1;
  227.                     cellDataLen = 2;
  228.                     LGetCell(&cellData, &cellDataLen, theCell, theList);
  229.                     theString = *strings + (*groupArray)[cellData].nameOffset;
  230.                     compare = CompareGroups(searchStr, theString);
  231.                     if (compare < 0) {
  232.                         high = theCell.v - 1;
  233.                     } else if (compare == 0) {
  234.                         if (low == theCell.v) {
  235.                             high = low;
  236.                         } else {
  237.                             high = theCell.v - 1;
  238.                         }
  239.                     } else {
  240.                         low = theCell.v + 1;
  241.                     }
  242.                 }
  243.                 if (low != high) break;
  244.                 theCell.v = low;
  245.                 cellDataLen = 2;
  246.                 LGetCell(&cellData, &cellDataLen, theCell, theList);
  247.                 theString = *strings + (*groupArray)[cellData].nameOffset;
  248.                 compare = CompareGroups(searchStr, theString);
  249.                 if (compare != 0) {
  250.                     theCell.v++;
  251.                     if (theCell.v >= (**theList).dataBounds.bottom) break;
  252.                     cellDataLen = 2;
  253.                     LGetCell(&cellData, &cellDataLen, theCell, theList);
  254.                     theString = *strings + (*groupArray)[cellData].nameOffset;
  255.                     compare = CompareGroups(searchStr, theString);
  256.                     if (compare != 0) break;
  257.                 }
  258.                 SetPt(&tmpCell,0,0);
  259.                 while (LGetSelect(true,&tmpCell,theList)) {
  260.                     LSetSelect(false,tmpCell,theList);
  261.                     tmpCell.v++;
  262.                 }
  263.                 LSetSelect(true,theCell,theList);
  264.                 LAutoScroll(theList);
  265.             } else {
  266.                 groupArray = (**info).groupArray;
  267.                 while (theCell.v < (**theList).dataBounds.bottom) {
  268.                     cellDataLen = 2;
  269.                     LGetCell(&cellData, &cellDataLen, theCell, theList);
  270.                     theString = *strings + (*groupArray)[cellData].nameOffset;
  271.                     compare = CompareGroups(searchStr, theString);
  272.                     if (compare == 0) {
  273.                         SetPt(&tmpCell,0,0);
  274.                         while (LGetSelect(true,&tmpCell,theList)) {
  275.                             LSetSelect(false,tmpCell,theList);
  276.                             tmpCell.v++;
  277.                         }
  278.                         LSetSelect(true,theCell,theList);
  279.                         LAutoScroll(theList);
  280.                         break;
  281.                     }
  282.                     theCell.v++;
  283.                 }
  284.             }
  285.             HUnlock(strings);
  286.             break;
  287.     }
  288. }
  289.  
  290.  
  291. /*    DoPagingCommand handles the paging commands: 
  292.     
  293.     Entry:    wind = pointer to window to be paged.
  294.             whichCmd =
  295.                 0: line up.
  296.                 1: line down. 
  297.                 2: page up.
  298.                 3: page down.
  299.                 4: home.
  300.                 5: end.
  301.                 6: next section.
  302.                 7: previous section.
  303. */
  304.  
  305. static void DoPagingCommand (WindowPtr wind, short whichCmd)
  306. {
  307.     TWindow **info;
  308.     ListHandle theList;
  309.     short kind, height, numCells;
  310.     
  311.     info = (TWindow**)GetWRefCon(wind);
  312.     kind = (**info).kind;
  313.     
  314.     if (kind == kFullGroup || kind == kNewGroup || kind == kUserGroup ||
  315.         kind == kSubject) 
  316.     {
  317.         theList = (**info).theList;
  318.         height = (**theList).visible.bottom - (**theList).visible.top;
  319.         numCells = (**theList).dataBounds.bottom;
  320.         switch (whichCmd) {
  321.             case 0:
  322.                 DoListArrow(wind, true);
  323.                 break;
  324.             case 1:
  325.                 DoListArrow(wind, false);
  326.                 break;
  327.             case 2:
  328.                 LScroll(0, -(height - 1), theList);
  329.                 break;
  330.             case 3:
  331.                 LScroll(0, height - 1, theList);
  332.                 break;
  333.             case 4:
  334.                 LScroll(0, -numCells, theList);
  335.                 break;
  336.             case 5:
  337.                 LScroll(0, numCells, theList);
  338.                 break;
  339.         }
  340.     } else { /* text window */
  341.         switch (whichCmd) {
  342.             case 0:
  343.                 ScrollTextLineUp(wind);
  344.                 break;
  345.             case 1:
  346.                 ScrollTextLineDown(wind);
  347.                 break;
  348.             case 2:
  349.                 ScrollTextPageUp(wind);
  350.                 break;
  351.             case 3:
  352.                 ScrollTextPageDown(wind);
  353.                 break;
  354.             case 4:
  355.                 ScrollTextHome(wind);
  356.                 break;
  357.             case 5:
  358.                 ScrollTextEnd(wind);
  359.                 break;
  360.             case 6:
  361.                 ScrollTextRight(wind);
  362.                 break;
  363.             case 7:
  364.                 ScrollTextLeft(wind);
  365.                 break;
  366.         }
  367.     }
  368. }
  369.  
  370.  
  371. /*    HandleKeypadKey handles numeric keypad command shortcuts, plus the four
  372.     paging keys on the extended keyboard. */
  373.  
  374. static Boolean HandleKeypadKey (WindowPtr wind, EWindowKind kind, EventRecord *ev)
  375. {
  376.     unsigned short theChar;
  377.     unsigned short keyCode;
  378.     Boolean isListWind, isListOrArticleWind, isArticleWind, x;
  379.     
  380.     theChar = ev->message & charCodeMask;
  381.     keyCode = (ev->message & keyCodeMask) >> 8;
  382.     
  383.     isListWind = kind == kFullGroup || kind == kNewGroup ||
  384.         kind == kUserGroup || kind == kSubject;
  385.     isListOrArticleWind = isListWind || kind == kArticle;
  386.     isArticleWind = kind == kArticle || kind == kMiscArticle;
  387.     x = gPrefs.keypadShortcuts;
  388.     
  389.     if (theChar == '0' && keyCode == 0x52 && isListOrArticleWind && x) {
  390.         DoNextArticle(wind, false);
  391.     } else if (theChar == '.' && keyCode == 0x41 && isListOrArticleWind && x) {
  392.         DoNextThread(wind);
  393.     } else if (theChar == enterKey && keyCode == 0x4c && isListOrArticleWind && x) {
  394.         DoNextGroup(wind);
  395.     } else if (theChar == '*' && (keyCode == 0x42 || keyCode == 0x43) && x) {
  396.         if (kind == kFullGroup) {
  397.             ShowHideGroups();
  398.         } else {
  399.             DoCloseWindow(wind);
  400.         }
  401.     } else if (theChar == '+' && (keyCode == 0x46 || keyCode == 0x45) && 
  402.         isListOrArticleWind && x) {
  403.         DoMarkCommand(wind, true);
  404.     } else if (theChar == '-' && keyCode == 0x4e && isListOrArticleWind && x) {
  405.         DoMarkCommand(wind, false);
  406.     } else if (theChar == '=' && (keyCode == 0x48 || keyCode == 0x51) && x) {
  407.         DoSelectAll(wind);
  408.     } else if (theChar == '/' && (keyCode == 0x4d || keyCode == 0x4b) &&
  409.         kind == kSubject && x) {
  410.         DoExpandCollapseSelectedThread(wind);
  411.     } else if (theChar == '8' && keyCode == 0x5b && x) {
  412.         DoPagingCommand(wind, 0);
  413.     } else if (theChar == '2' && keyCode == 0x54 && x) {
  414.         DoPagingCommand(wind, 1);
  415.     } else if (theChar == pageUpKey || (theChar == '9' && keyCode == 0x5c)) {
  416.         DoPagingCommand(wind, 2);
  417.     } else if (theChar == pageDownKey || (theChar == '3' && keyCode == 0x55)) {
  418.         DoPagingCommand(wind, 3);
  419.     } else if (theChar == homeKey || (theChar == '7' && keyCode == 0x59)) {
  420.         DoPagingCommand(wind, 4);
  421.     } else if (theChar == endKey || (theChar == '1' && keyCode == 0x53)) {
  422.         DoPagingCommand(wind, 5);
  423.     } else if (theChar == '6' && keyCode == 0x58 && isArticleWind && x) {
  424.         DoPagingCommand(wind, 6);
  425.     } else if (theChar == '4' && keyCode == 0x56 && isArticleWind && x) {
  426.         DoPagingCommand(wind, 7);
  427.     } else {
  428.         return false;
  429.     }
  430.     
  431.     return true;
  432. }
  433.  
  434.  
  435. /* DoForwardDelete handles the forward delete key. */
  436.  
  437. static void DoForwardDelete (WindowPtr wind)
  438. {
  439.     TWindow **info;
  440.     TEHandle theTE;
  441.     short selStart, selEnd, teLength;
  442.     
  443.     info = (TWindow**)GetWRefCon(wind);
  444.     theTE = (**info).theTE;
  445.     selStart = (**theTE).selStart;
  446.     selEnd = (**theTE).selEnd;
  447.     teLength = (**theTE).teLength;
  448.     if (selStart == selEnd && selEnd < teLength) (**theTE).selEnd = selEnd + 1;
  449.     TEDelete(theTE);
  450.     AdjustScrollBar(wind);
  451.     CheckInsertion(wind);
  452. }
  453.  
  454.  
  455. /*    HandleKeyDown handles key down events */
  456.  
  457. void HandleKeyDown (WindowPtr wind, EventRecord *ev)
  458. {
  459.     unsigned short theChar;
  460.     TWindow **info;
  461.     EWindowKind kind;
  462.     long menucmd = 0;
  463.     
  464.     theChar = ev->message & charCodeMask;
  465.     if ((ev->modifiers & cmdKey) != 0) {
  466.         menucmd = MenuKey(theChar);
  467.         if (HiWord(menucmd))
  468.             DoCommand(MenuKey(theChar));
  469.         else
  470.             menucmd = 0;
  471.     }
  472.     if (!menucmd && IsAppWindow(wind)) {
  473.         ObscureCursor();
  474.         info = (TWindow**)GetWRefCon(wind);
  475.         kind = (**info).kind;
  476.         if (HandleKeypadKey(wind, kind, ev)) return;
  477.         switch (kind) {
  478.             case kFullGroup:
  479.             case kNewGroup:
  480.             case kUserGroup:
  481.             case kSubject:
  482.                 HandleListKey(wind, theChar, ev->modifiers);
  483.                 break;
  484.             case kArticle:
  485.             case kMiscArticle:
  486.                 if (theChar == leftArrow || theChar == rightArrow ||
  487.                     theChar == upArrow || theChar == downArrow) {
  488.                     TEKey(theChar,(**info).theTE);
  489.                     CheckInsertion(wind);
  490.                 }
  491.                 break;
  492.             case kPostMessage:
  493.             case kMailMessage:
  494.                 if (theChar == forwardDelKey) {
  495.                     DoForwardDelete(wind);
  496.                 } else if (!(ev->modifiers & cmdKey)) {
  497.                     TEKey(theChar,(**info).theTE);
  498.                     AdjustScrollBar(wind);
  499.                     CheckInsertion(wind);
  500.                 }
  501.                 break;
  502.         }
  503.     }
  504. }
  505.  
  506.